<?php

/**
 * 简单数学方法
 *
 * @author Administrator
 */
class SampleMath {

    /**
     * 获取最密集数字串keys
     * @param array $orgs       原数组
     * @param int $m            串长度
     * @param int $needType     需要返回类型 1 key-values 2 keys 3 values
     */
    public static function bestNear($orgs, $m = 3, $nearNum = 0, $needType = 1) {
        if (count($orgs) <= $m) {
            return $orgs;
        }

        $tmp = $orgs;
        asort($orgs); // 保持索引关系
        sort($tmp);

        $btNums = [];
        $btNumsSum = [];
        $index = 0;
        foreach ($tmp as $num) {
            if ($index <= count($tmp) - $m) {
                $btNums[$index] = $tmp[$index + $m - 1] - $num;
                $btNumsSum[$index] = array_sum(array_slice($tmp, $index, $m, true));
            }
            $index ++;
        }

        if (empty($btNums)) {
            return $orgs;
        }

        asort($btNums); // 保持索引关系
        $best = [];
        if ($nearNum == 0) {
            $best = array_slice($btNums, 0, 1, true);
        } else {
            $best = self::_nearAbsNumArr($nearNum, $btNumsSum);
        }
        $bestKey = key($best);

        $bestNears = array();
        $j = 0;
        foreach ($orgs as $key => $value) {
            if ($j >= $bestKey && $j < $bestKey + $m) {
                $bestNears[$key] = $value;
            }
            $j ++;
        }

        switch ($needType) {
            case 1:
                return $bestNears;
            case 2:
                return array_keys($bestNears);
            case 3:
                return array_values($bestNears);
            default :
                return $bestNears;
        }
    }

    /**
     * 获取最密集数字串keys（倒序取）
     * @param array $orgs       原数组
     * @param int $m            串长度
     * @param int $nearNum      趋近数值
     * @param int $needType     需要返回类型 1 key-values 2 keys 3 values
     */
    public static function bestNear4Desc($orgs, $m = 3, $nearNum = 0, $needType = 1) {
        if (count($orgs) <= $m) {
            return $orgs;
        }

        $tmp = $orgs;
        arsort($orgs); // 保持索引关系
        rsort($tmp);

        $btNums = [];
        $btNumsSum = [];
        $index = 0;
        foreach ($tmp as $num) {
            if ($index <= count($tmp) - $m) {
                $btNums[$index] = $num - $tmp[$index + $m - 1];
                $btNumsSum[$index] = array_sum(array_slice($tmp, $index, $m, true));
            }
            $index ++;
        }

        if (empty($btNums)) {
            return $orgs;
        }

        asort($btNums); // 保持索引关系
        $bestKey = -1;
        if ($nearNum == 0) {
            $bestKey = key(array_slice($btNums, 0, 1, true));
        } else {
            $bestKey = self::_nearAbsNumArr($nearNum, $btNumsSum);
        }

        $bestNears = array();
        $j = 0;
        foreach ($orgs as $key => $value) {
            if ($j >= $bestKey && $j < $bestKey + $m) {
                $bestNears[$key] = $value;
            }
            $j ++;
        }

        switch ($needType) {
            case 1:
                return $bestNears;
            case 2:
                return array_keys($bestNears);
            case 3:
                return array_values($bestNears);
            default :
                return $bestNears;
        }
    }

    /**
     * 和值趋近指定值
     * @param int/float $nearNum        指定值
     * @param array $numArr             数组串
     * @return int                      趋近指定值下标
     */
    private static function _nearAbsNumArr($nearNum, $numArr) {
        $w = 0;
        $c = -1;
        $abstand = 0;
        $l = count($numArr);
        for ($pos = 0; $pos < $l; $pos++) {
            $n = $numArr[$pos];
            $abstand = abs($nearNum - $n);
            if ($c == -1) {
                $c = $abstand;
                continue;
            } else if ($abstand < $c) {
                $c = $abstand;
                $w = $pos;
            }
        }
        return $w;
    }

}
